package com.wstuo.itsm.request.utils;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import com.wstuo.common.security.dto.WorkingTimeDTO;
import com.wstuo.common.security.entity.Holiday;
import com.wstuo.common.security.entity.ServiceTime;
import com.wstuo.common.util.TimeUtils;

/**
 * SLA时间计算主函数.
 * 
 * @author QXY
 * 
 */
public class CalcPredictTimeUtils {
	

	/**
	 * 计算时间主函数.
	 * @param slaResponse SLA响应时间，如3小时、5小时
	 * @param createdTime 请求的创建时间
	 * @param workingTimeDTO 工作时间(节假日及服务时间)
	 * @param pendingDateStart 排除指定时间段的开始时间
	 * @param pendingDateEnd 排除指定时间段的结束时间
	 * @return Long 返回计算后时间Long
	 */
	public Long calcPredictTime(Long slaResponse, 
			Long createdTime,
			WorkingTimeDTO workingTimeDTO,
			Date pendingDateStart, 
			Date pendingDateEnd) {
		// 如果下班时间为0时0分，那么修改为23时59秒
		ServiceTime serviceTime = workingTimeDTO.getServiceTime();
		if (serviceTime.getEndHour() == 0L && serviceTime.getEndMinute() == 0L) {
			serviceTime.setEndHour(23);
			serviceTime.setEndMinute(59);
		}

		// 工作时间是全天的
		if (serviceTime.getAllday()) {
			serviceTime.setStartHour(0);
			serviceTime.setStartMinute(0);
			serviceTime.setEndHour(24);
			serviceTime.setEndMinute(0);
		}
		List<Holiday> holidays = workingTimeDTO.getHolidays();
		// 调用方法合并非工作时间
		List<Date> offdays = mergeHoliday(createdTime, serviceTime, holidays);

		// 上班前创建的
		if (!dateContains(offdays, new Date(createdTime))
				&& TimeUtils.getTimeOfDay(new Date(createdTime)) < serviceTime
						.getStartTime()) {
			createdTime += serviceTime.getStartTime()
					- TimeUtils.getTimeOfDay(new Date(createdTime));// 上班即处理
		}
		//中午创建的
		if(!dateContains(offdays,new Date(createdTime)) 
				&& TimeUtils.getTimeOfDay(new Date(createdTime))>serviceTime.getStartNoonTime()
				&& TimeUtils.getTimeOfDay(new Date(createdTime))<serviceTime.getEndNoonTime()){
			createdTime+=serviceTime.getEndNoonTime()-TimeUtils.getTimeOfDay(new Date(createdTime));//上班即处理
		}
		// 计算实际挂起时间
		if (pendingDateStart != null && pendingDateEnd != null) {
			// 调用方法合并非工作时间
			List<Date> offday1 = mergeHolidays(createdTime, serviceTime,holidays);
			Long pendingTimeLong = calcPendingTimeLong(pendingDateStart,pendingDateEnd, serviceTime, offday1);
			if (pendingTimeLong > 0)
				slaResponse = slaResponse + pendingTimeLong;
		}
		// 当天可以响应，但无法完成
		if (!dateContains(offdays, new Date(createdTime))
				&& TimeUtils.getTimeOfDay(new Date(createdTime)) >= serviceTime
						.getStartTime()
				&& TimeUtils.getTimeOfDay(new Date(createdTime)) <= serviceTime
						.getEndTime()
				&& slaResponse > serviceTime.getEndTime()
						- TimeUtils.getTimeOfDay(new Date(createdTime))) {
			offdays.add(0, new Date(createdTime));// 把当天当做节假日
			slaResponse -= serviceTime.getEndTime()
					- TimeUtils.getTimeOfDay(new Date(createdTime));// 响应时间=响应时间-已经处理的时间
		}else if(!dateContains(offdays,new Date(createdTime)) 
				&& TimeUtils.getTimeOfDay(new Date(createdTime))>=serviceTime.getStartTime() 
				&& TimeUtils.getTimeOfDay(new Date(createdTime))<=serviceTime.getStartNoonTime()
				&& slaResponse>(serviceTime.getEndTime()-TimeUtils.getTimeOfDay(new Date(createdTime))-(serviceTime.getEndNoonTime()-serviceTime.getStartNoonTime()))){
				offdays.add(0,new Date(createdTime));//把当天当做节假日
				slaResponse-=serviceTime.getEndTime()-TimeUtils.getTimeOfDay(new Date(createdTime))-(serviceTime.getEndNoonTime()-serviceTime.getStartNoonTime());//响应时间=响应时间-已经处理的时间
		}

		// 根据创建时间往后推移
		for (Date date : offdays) {
			if (TimeUtils.equalTimeIsConsistent(date, createdTime)) {
				Calendar cl = new GregorianCalendar();
				cl.setTimeInMillis(createdTime);
				cl.set(Calendar.DATE, cl.get(Calendar.DATE) + 1);
				cl.set(Calendar.HOUR_OF_DAY, serviceTime.getStartHour());
				cl.set(Calendar.MINUTE, serviceTime.getStartMinute());
				cl.set(Calendar.SECOND, 0);
				createdTime = cl.getTimeInMillis();
			}
		}
		// 实际开始服务时间
		Long realServiceTime = createdTime;
		// 服务时间里的工时
//		Long workTimes = serviceTime.getEndTime() - serviceTime.getStartTime();
		Long workTimes =(serviceTime.getStartNoonTime()-serviceTime.getStartTime())+(serviceTime.getEndTime()-serviceTime.getEndNoonTime());
		// 预估要几个工作日
		Long workDays = slaResponse / workTimes;
		// 预估最后一个工作日需花多少时间
		Long lastTimes = slaResponse % workTimes;
		// 加上预估的工作日的全天时间
		if (workDays > 0) {
			realServiceTime += workDays * ServiceTime.DAY_MILLISECONDS;
		}
		Long NoonTimes=serviceTime.getStartNoonTime()-serviceTime.getStartTime();
		if(lastTimes>0){
			realServiceTime += lastTimes;
		}
		//SLA超过当天中午的时候
		if(TimeUtils.getTimeOfDay(new Date(createdTime))<=serviceTime.getStartNoonTime()
			&& serviceTime.getStartNoonTime()<(slaResponse+TimeUtils.getTimeOfDay(new Date(createdTime)))
			&& workDays==0 && !serviceTime.getAllday()){
			realServiceTime+=(serviceTime.getEndNoonTime()-serviceTime.getStartNoonTime());
		}
		if(workDays > 0 && lastTimes>NoonTimes && !serviceTime.getAllday()){
			realServiceTime += (serviceTime.getEndNoonTime()-serviceTime.getStartNoonTime());
		}
		// 排除节假日往后推
		for (Date date : offdays) {
			if (TimeUtils.equalTimeIsConsistent(date, realServiceTime)) {
				realServiceTime += ServiceTime.DAY_MILLISECONDS;
			}
		}
		return realServiceTime;
	}

	/**
	 * 比较列表中是否存在相同的日期
	 * @param dateList 时间集合
	 * @param compile 要比较的时间
	 * @return Boolean 返回是否存在相同日期的判断结果
	 */
	private Boolean dateContains(List<Date> dateList, Date compile) {
		if (dateList != null && dateList.size() > 0) {
			Calendar dateCompile = new GregorianCalendar();
			dateCompile.setTime(compile);
			
			int YEAR = dateCompile.get(Calendar.YEAR);
			int MONTH = dateCompile.get(Calendar.MONTH);
			int DATE = dateCompile.get(Calendar.DATE);
			
			for (Date date : dateList) {
				Calendar dateCl = new GregorianCalendar();
				dateCl.setTime(date);
				// 判断日期是否一样
				if (YEAR == dateCl.get(Calendar.YEAR)
						&& MONTH == dateCl.get(Calendar.MONTH)
						&& DATE == dateCl.get(Calendar.DATE)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * 根据服务时间和节假日合并得到非工作时间(根据创建时间及8周)
	 * @param serviceTime 服务时间
	 * @param holidays 节假日
	 * @return 返回非工作日期集合
	 */
	public List<Date> mergeHoliday(Long createdTime, ServiceTime serviceTime,
			List<Holiday> holidays) {

		// 工作日
		List<Integer> offDays = null;
		if (serviceTime != null) {
			offDays = new ArrayList<Integer>();
			if (serviceTime.getMonday() != null && !serviceTime.getMonday())
				offDays.add(2);
			if (serviceTime.getTuesday() != null && !serviceTime.getTuesday())
				offDays.add(3);
			if (serviceTime.getWednesday() != null && !serviceTime.getWednesday())
				offDays.add(4);
			if (serviceTime.getThursday() != null && !serviceTime.getThursday())
				offDays.add(5);
			if (serviceTime.getFriday() != null && !serviceTime.getFriday())
				offDays.add(6);
			if (serviceTime.getSaturday() != null && !serviceTime.getSaturday())
				offDays.add(7);
			if (serviceTime.getSunday() != null && !serviceTime.getSunday())
				offDays.add(1);
		}

		List<Date> offDates = new ArrayList<Date>();
		// 创建时候为非工作时间也把当天当做非工作时间
		Long createdTimeLong = TimeUtils.getTimeOfDay(new Date(createdTime));

		if (serviceTime!=null && createdTimeLong >= serviceTime.getEndTime()) {
			Calendar c = new GregorianCalendar();
			c.setTimeInMillis(createdTime);
			c.set(Calendar.HOUR_OF_DAY, 0);
			c.set(Calendar.MINUTE, 0);
			c.set(Calendar.SECOND, 0);
			offDates.add(c.getTime());
		}

		if (offDays != null) {
			for (Integer offday : offDays) {
				// 从设置中取出非工作日
				Calendar c = new GregorianCalendar();
				// 设置添加未来8周内的非工作日
				for (int i = 0; i < 8; i++) {
					Long nextWeek = i * (60 * 60 * 24 * 1000 * 7L);
					c.setTime(new Date(createdTime + nextWeek));
					c.set(Calendar.DAY_OF_WEEK, offday);
					c.set(Calendar.HOUR_OF_DAY, 0);
					c.set(Calendar.MINUTE, 0);
					c.set(Calendar.SECOND, 0);

					// 过滤重复的
					if (!dateContains(offDates, c.getTime())) {
						offDates.add(c.getTime());
					}
				}
			}
		}
		// 合并节假日
		if (holidays != null && holidays.size() > 0) {
			// 加入假期
			for (Holiday hd : holidays) {
				// 过滤重复的
				if (!dateContains(offDates, hd.getHdate())) {
					offDates.add(hd.getHdate());
				}
			}
		}
		Collections.sort(offDates);// 排序
		return offDates;
	}

	/**
	 * 根据服务时间和节假日合并得到非工作时间(根据创建时间及5周)
	 * @param serviceTime 服务时间
	 * @param holidays 节假日
	 * @return List<Date> 非工作时间集合
	 */
	public List<Date> mergeHolidays(Long createdTime, ServiceTime serviceTime,
			List<Holiday> holidays) {
		// 工作日
		List<Integer> offDays = null;
		if (serviceTime != null) {
			offDays = new ArrayList<Integer>();
			if (!serviceTime.getMonday())
				offDays.add(2);
			if (!serviceTime.getTuesday())
				offDays.add(3);
			if (!serviceTime.getWednesday())
				offDays.add(4);
			if (!serviceTime.getThursday())
				offDays.add(5);
			if (!serviceTime.getFriday())
				offDays.add(6);
			if (!serviceTime.getSaturday())
				offDays.add(7);
			if (!serviceTime.getSunday())
				offDays.add(1);
		}
		List<Date> offDates = new ArrayList<Date>();
		// 创建时候为非工作时间也把当天当做非工作时间
		Long createdTimeLong = TimeUtils.getTimeOfDay(new Date(createdTime));
		if (serviceTime!=null && createdTimeLong >= serviceTime.getEndTime()) {
			Calendar c = new GregorianCalendar();
			c.setTimeInMillis(createdTime);
			c.set(Calendar.HOUR_OF_DAY, 0);
			c.set(Calendar.MINUTE, 0);
			c.set(Calendar.SECOND, 0);
			offDates.add(c.getTime());
		}
		if (offDays != null) {
			for (Integer offday : offDays) {
				// 从设置中取出非工作日
				Calendar c = new GregorianCalendar();
				// 设置添加未来3周内的非工作日
				for (int i = 0; i < 5; i++) {
					Long nextWeek = i * (60 * 60 * 24 * 1000 * 7L);
					c.setTime(new Date(createdTime + nextWeek));
					c.set(Calendar.DAY_OF_WEEK, offday);
					c.set(Calendar.HOUR_OF_DAY, 0);
					c.set(Calendar.MINUTE, 0);
					c.set(Calendar.SECOND, 0);
					// 过滤重复的
					if (!dateContains(offDates, c.getTime())) {
						offDates.add(c.getTime());
					}
				}
			}
		}
		// 合并节假日
		if (holidays != null && holidays.size() > 0) {
			// 加入假期
			for (Holiday hd : holidays) {
				// 过滤重复的
				if (!dateContains(offDates, hd.getHdate())) {
					offDates.add(hd.getHdate());
				}
			}
		}
		Collections.sort(offDates);// 排序
		return offDates;
	}

	

	/**
	 * 计算挂起时间有效时长
	 * @param pendingDateStart 挂起开始时间
	 * @param pendingDateEnd 挂起结束时间
	 * @param serviceTime 服务时间
	 * @param offdays 节假日
	 * @return 返回的有效时长，毫秒
	 */
	public Long calcPendingTimeLong(Date pendingDateStart, Date pendingDateEnd,
			ServiceTime serviceTime, List<Date> offdays) {
		Long pendingTimeLong = 0L;// 返回的有效时长
		//挂起开始时间和挂起结束时间不允许为空，并且挂起开始时间和挂起结束时间不相等
		if (pendingDateStart != null && pendingDateEnd != null
				&& pendingDateStart.getTime() != pendingDateEnd.getTime()) {
			//得到挂起开始时间和挂起结束时间之间的全部日期
			Date[] dates = getDateArrays(pendingDateStart, pendingDateEnd,
					Calendar.DAY_OF_MONTH);
			if (dates.length == 1) {// 如果只有一个日期，则挂起时间是否同一天
				// 判断是否在节假日内
				boolean isInOffdays = false;
				for (Date date : offdays) {// 判断当前时间是否在节假日内
					if (TimeUtils.equalTimeIsConsistent(date, dates[0].getTime())) {
						isInOffdays = true;
						break;
					}
				}
				// 不在节假日内，并且挂起开始时间小于服务的结束时间，并且挂起结束时间大于等于服务开始时间
				if (!isInOffdays
						&& TimeUtils.getTimeOfDays(pendingDateStart) < serviceTime
								.getEndTime()
						&& TimeUtils.getTimeOfDays(pendingDateEnd) >= serviceTime
								.getStartTime()) {
					if (TimeUtils.getTimeOfDays(pendingDateStart) < serviceTime
							.getStartTime()) {// 如果挂起开始时间小于服务开始时间，把挂起开始时间修改成服务开始时间
						Calendar cl = new GregorianCalendar();
						cl.setTime(pendingDateStart);
						cl.set(Calendar.HOUR_OF_DAY, serviceTime.getStartHour());
						cl.set(Calendar.MINUTE, serviceTime.getStartMinute());
						cl.set(Calendar.SECOND, 0);
						pendingDateStart = cl.getTime();
					}
					if (TimeUtils.getTimeOfDays(pendingDateEnd) > serviceTime
							.getEndTime()) {// 如果挂起结束时间小于服务结束时间，把挂起结束时间修改成服务结束时间
						Calendar cl = new GregorianCalendar();
						cl.setTime(pendingDateEnd);
						cl.set(Calendar.HOUR_OF_DAY, serviceTime.getEndHour());
						cl.set(Calendar.MINUTE, serviceTime.getEndMinute());
						cl.set(Calendar.SECOND, 0);
						pendingDateEnd = cl.getTime();
					}
					pendingTimeLong = (pendingDateEnd.getTime() - pendingDateStart
							.getTime());
				}
			} else {
				for (Date d : dates) {
					// 判断是否节假日
					boolean isInOffdays = false;
					for (Date date : offdays) {
						if (TimeUtils.equalTimeIsConsistent(date, d.getTime())) {
							isInOffdays = true;
						}
					}

					if (!isInOffdays) {
						if (d.getTime() == pendingDateStart.getTime()) {
							Date tempDate = d;
							if (TimeUtils.getTimeOfDays(d) < serviceTime.getStartTime()) {
								Calendar cl = new GregorianCalendar();
								cl.setTime(d);
								cl.set(Calendar.HOUR_OF_DAY,
										serviceTime.getStartHour());
								cl.set(Calendar.MINUTE,
										serviceTime.getStartMinute());
								cl.set(Calendar.SECOND, 0);
								tempDate = cl.getTime();
							}
							if (TimeUtils.getTimeOfDays(d) > serviceTime.getEndTime()) {
								Calendar cl = new GregorianCalendar();
								cl.setTime(d);
								cl.set(Calendar.HOUR_OF_DAY,
										serviceTime.getEndHour());
								cl.set(Calendar.MINUTE,
										serviceTime.getEndMinute());
								cl.set(Calendar.SECOND, 0);
								tempDate = cl.getTime();
							}
							Calendar cls = new GregorianCalendar();
							cls.setTime(d);
							cls.set(Calendar.HOUR_OF_DAY,
									serviceTime.getEndHour());
							cls.set(Calendar.MINUTE, serviceTime.getEndMinute());
							cls.set(Calendar.SECOND, 0);
							Long timeLong = (cls.getTime().getTime() - tempDate
									.getTime());
							if (timeLong > 0)
								pendingTimeLong += timeLong;
						} else if (d.getTime() == pendingDateEnd.getTime()) {
							Calendar cls = new GregorianCalendar();
							cls.setTime(d);
							cls.set(Calendar.HOUR_OF_DAY,
									serviceTime.getStartHour());
							cls.set(Calendar.MINUTE,
									serviceTime.getStartMinute());
							cls.set(Calendar.SECOND, 0);

							if (TimeUtils.getTimeOfDays(d) < serviceTime.getStartTime()) {
								Calendar cl = new GregorianCalendar();
								cl.setTime(pendingDateStart);
								cl.set(Calendar.HOUR_OF_DAY,
										serviceTime.getStartHour());
								cl.set(Calendar.MINUTE,
										serviceTime.getStartMinute());
								cl.set(Calendar.SECOND, 0);
								pendingDateStart = cl.getTime();
							}
							if (TimeUtils.getTimeOfDays(d) > serviceTime.getEndTime()) {
								Calendar cl = new GregorianCalendar();
								cl.setTime(pendingDateEnd);
								cl.set(Calendar.HOUR_OF_DAY,
										serviceTime.getEndHour());
								cl.set(Calendar.MINUTE,
										serviceTime.getEndMinute());
								cl.set(Calendar.SECOND, 0);
								pendingDateEnd = cl.getTime();
							}
							Long timeLong = (d.getTime() - cls.getTime()
									.getTime());
							if (timeLong > 0)
								pendingTimeLong += timeLong;
						} else {
							Calendar cls = new GregorianCalendar();
							cls.setTime(d);
							cls.set(Calendar.HOUR_OF_DAY,
									serviceTime.getStartHour());
							cls.set(Calendar.MINUTE,
									serviceTime.getStartMinute());
							cls.set(Calendar.SECOND, 0);
							pendingDateStart = cls.getTime();

							Calendar cle = new GregorianCalendar();
							cle.setTime(d);
							cle.set(Calendar.HOUR_OF_DAY,
									serviceTime.getEndHour());
							cle.set(Calendar.MINUTE, serviceTime.getEndMinute());
							cle.set(Calendar.SECOND, 0);
							pendingDateEnd = cle.getTime();
							pendingTimeLong += pendingDateEnd.getTime()
									- pendingDateStart.getTime();
						}
					}
				}
			}

		}
		return pendingTimeLong;
	}

	/**
	 * 根据开始时间、结束时间得到两个时间段内所有的日期
	 * @param start 开始日期
	 * @param end 结束日期
	 * @param calendarType 类型
	 * @return Date[] 两个日期之间的日期
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private Date[] getDateArrays(Date start, Date end, int calendarType) {
		List ret = new ArrayList();
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(start);
		Date tmpDate = calendar.getTime();
		long endTime = end.getTime();
		while (tmpDate.before(end) || tmpDate.getTime() == endTime) {
			ret.add(calendar.getTime());
			calendar.add(calendarType, 1);
			tmpDate = calendar.getTime();
		}
		ret.set(ret.size() - 1, end);
		Date[] dates = new Date[ret.size()];
		return (Date[]) ret.toArray(dates);
	}

}
